package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

type CmdResult struct {
	Success  bool   `json:"success"`
	Data     string `json:"data"`
	ErrorMsg string `json:"errorMsg"`
}

var ctx = context.Background()

func redisCliGet(rdb *MyRedisClient, key string) (string, error) {
	if rdb.cluster {
		return rdb.clusterClient.Get(ctx, key).Result()
	} else {
		return rdb.client.Get(ctx, key).Result()
	}
	// Output: key value
	// key2 does not exist
}

func redisCliSet(rdb *MyRedisClient, key string, value string) (string, error) {
	if rdb.cluster {
		return rdb.clusterClient.Set(ctx, key, value, 0).Result()
	} else {
		return rdb.client.Set(ctx, key, value, 0).Result()
	}
	// Output: key value
	// key2 does not exist
}

func redisCliInfo(rdb *MyRedisClient) (string, error) {
	if rdb.cluster {
		return rdb.clusterClient.Info(ctx, "clients").Result()
	} else {
		return rdb.client.Info(ctx, "clients").Result()
	}
	// Output: key value
	// key2 does not exist
}

func redisCliKeys(rdb *MyRedisClient, key string) (string, error) {
	var val []string
	var err error
	if rdb.cluster {
		val, err = rdb.clusterClient.Keys(ctx, key).Result()
	} else {
		val, err = rdb.client.Keys(ctx, key).Result()
	}
	fmt.Println(err)
	//val, err := rdb.Keys(ctx, key).Result()
	result := "["
	for i := 0; i < len(val); i++ {
		result = result + "," + val[i]
	}
	result = result + "]"
	return result, err
	// Output: key value
	// key2 does not exist
}
func redisClihGet(rdb *MyRedisClient, key string, name string) (string, error) {
	if rdb.cluster {
		return rdb.clusterClient.HGet(ctx, key, name).Result()
	} else {
		return rdb.client.HGet(ctx, key, name).Result()
	}

	// Output: key value
	// key2 does not exist
}

func redisClihSet(rdb *MyRedisClient, key string, name string, value string) (string, error) {
	if rdb.cluster {
		state, err := rdb.clusterClient.HSet(ctx, key, name, value).Result()
		return strconv.FormatInt(state, 10), err
	} else {
		state, err := rdb.client.HSet(ctx, key, name, value).Result()
		return strconv.FormatInt(state, 10), err
	}
	// Output: key value
	// key2 does not exist
}

func redisCliDel(rdb *MyRedisClient, key string) (string, error) {
	var val []string
	var err error
	if rdb.cluster {
		val, err = rdb.clusterClient.Keys(ctx, key).Result()
		for i := 0; i < len(val); i++ {
			childKey := val[i]
			logrus.Info("del redisKey: " + childKey)
			rdb.clusterClient.Del(ctx, childKey)
		}
	} else {
		val, err = rdb.client.Keys(ctx, key).Result()
		for i := 0; i < len(val); i++ {
			childKey := val[i]
			logrus.Info("del redisKey: " + childKey)
			rdb.client.Del(ctx, childKey)
		}
	}
	result := "删除: " + key + " 成功"
	return result, err
}

func redisCliFlushAll(rdb *MyRedisClient) (string, error) {
	if rdb.cluster {
		return rdb.clusterClient.FlushAll(ctx).Result()
	} else {
		return rdb.client.FlushAll(ctx).Result()
	}
}

func redisCliType(rdb *MyRedisClient, key string) (string, error) {
	if rdb.cluster {
		return rdb.clusterClient.Type(ctx, key).Result()
	} else {
		return rdb.client.Type(ctx, key).Result()
	}
}

func redisCliHGetAll(rdb *MyRedisClient, key string) (string, error) {
	var val map[string]string
	var err error
	if rdb.cluster {
		val, err = rdb.clusterClient.HGetAll(ctx, key).Result()
	} else {
		val, err = rdb.client.HGetAll(ctx, key).Result()
	}
	result := "["
	for s, s2 := range val {
		result = result + "," + s + ":" + s2
	}
	result = result + "]"
	return result, err
}

func processResult(result string, err error, w http.ResponseWriter) {
	cmdResult := CmdResult{
		Success: false,
		Data:    "",
	}
	if err != nil {
		//panic(err)
		logrus.Error("redis err:" + err.Error())
		cmdResult.Success = false
		cmdResult.ErrorMsg = err.Error()
	} else {
		cmdResult.Success = true
	}
	logrus.Info("redis cmd result:", result)
	cmdResult.Data = result

	//p := &Product{}
	//err := json.Unmarshal([]byte(result), p)

	sysJson, _ := json.Marshal(cmdResult)

	jsonInfo := string(sysJson)

	cmdResult.Data = jsonInfo

	w.Write(sysJson)

}
func operation(w http.ResponseWriter, req *http.Request) {
	fmt.Println("0---")
	params := req.URL.Query()
	host := params.Get("host")
	port := params.Get("port")
	cmd := params.Get("cmd")
	password := params.Get("password")
	clusterInfo := params.Get("clusterInfo")
	logrus.Info("请求url:" + req.URL.RequestURI())

	//client := newClient(host, port, password)
	client2 := myMyRedisClient(host, port, password, clusterInfo)
	if cmd == "keys" {
		keys := params.Get("keys")
		val, err := redisCliKeys(client2, keys)
		processResult(val, err, w)
	} else if cmd == "hget" {
		key := params.Get("key")
		name := params.Get("name")
		val, err := redisClihGet(client2, key, name)
		processResult(val, err, w)
	} else if cmd == "hset" {
		key := params.Get("key")
		name := params.Get("name")
		value := params.Get("value")
		val, err := redisClihSet(client2, key, name, value)
		processResult(val, err, w)
	} else if cmd == "get" {
		key := params.Get("key")
		val, err := redisCliGet(client2, key)
		processResult(val, err, w)
	} else if cmd == "set" {
		key := params.Get("key")
		value := params.Get("value")
		val, err := redisCliSet(client2, key, value)
		processResult(val, err, w)
	} else if cmd == "info" {
		val, err := redisCliInfo(client2)
		processResult(val, err, w)
	} else if cmd == "del" {
		key := params.Get("keys")
		val, err := redisCliDel(client2, key)
		processResult(val, err, w)
	} else if cmd == "flushAll" {
		val, err := redisCliFlushAll(client2)
		processResult(val, err, w)
	} else if cmd == "type" {
		key := params.Get("key")
		val, err := redisCliType(client2, key)
		processResult(val, err, w)
	} else if cmd == "hGetAll" {
		key := params.Get("key")
		val, err := redisCliHGetAll(client2, key)
		processResult(val, err, w)
	} else {
		w.Write([]byte("not support cmd:" + cmd))
	}
}

type MyRedisClient struct {
	clusterClient *redis.ClusterClient
	client        *redis.Client
	cluster       bool
}

func myMyRedisClient(host string, port string, password string, clusterInfo string) *MyRedisClient {
	cluster := len(clusterInfo) > 0

	c := &MyRedisClient{
		//clusterClient: clusterClient,
		//client:        client,
		cluster: cluster,
	}

	if cluster {
		c.clusterClient = newClusterClient(password, clusterInfo)
	} else {
		c.client = newClient(host, port, password)
	}

	return c
}

func newClusterClient(password string, clusterInfo string) *redis.ClusterClient {
	logrus.Info("connect redis cluster:" + clusterInfo)
	var addrs = strings.Split(clusterInfo, ";")
	rdb := redis.NewClusterClient(&redis.ClusterOptions{
		Addrs:    addrs,
		Password: password,
	})
	return rdb
}
func newClient(host string, port string, password string) *redis.Client {
	logrus.Info("connect redis:" + host + ":" + port)
	rdb := redis.NewClient(&redis.Options{
		Addr:     host + ":" + port,
		Password: password, // no password set
		DB:       0,        // use default DB
	})
	return rdb
}

func newLfsHook(logName string, maxRemainCnt uint) logrus.Hook {
	writer, err := rotatelogs.New(
		logName+".%Y%m%d%H",
		// WithLinkName为最新的日志建立软连接，以方便随着找到当前日志文件
		rotatelogs.WithLinkName(logName),

		// WithRotationTime设置日志分割的时间，这里设置为一小时分割一次
		rotatelogs.WithRotationTime(time.Hour),

		// WithMaxAge和WithRotationCount二者只能设置一个，
		// WithMaxAge设置文件清理前的最长保存时间，
		// WithRotationCount设置文件清理前最多保存的个数。
		//rotatelogs.WithMaxAge(time.Hour*24),
		rotatelogs.WithRotationCount(maxRemainCnt),
	)

	if err != nil {
		logrus.Errorf("config local file system for logger error: %v", err)
	}

	logrus.SetLevel(logrus.InfoLevel)

	lfsHook := lfshook.NewHook(lfshook.WriterMap{
		logrus.DebugLevel: writer,
		logrus.InfoLevel:  writer,
		logrus.WarnLevel:  writer,
		logrus.ErrorLevel: writer,
		logrus.FatalLevel: writer,
		logrus.PanicLevel: writer,
	}, &logrus.TextFormatter{DisableColors: true})

	return lfsHook
}

func action(ctx *cli.Context) error {
	http.HandleFunc("/goredisweb", operation) //设置访问的路由
	logrus.Info("服务器正常启动:0.0.0.0:9091")
	err := http.ListenAndServe("0.0.0.0:9091", nil) //设置监听的端口
	if err != nil {
		//log.Fatal("ListenAndServe: ", err)
		logrus.Error(err)
	}
	//epoll
	select {}
}

func main() {

	hook := newLfsHook("./logs/app.log", 1000)
	logrus.AddHook(hook)

	app := new(cli.App)
	app.Name = "goredisweb"
	app.Version = "1.0"
	app.Usage = "run"
	app.Action = action
	err := app.Run(os.Args)
	if err != nil {
		logrus.Error(err)
	}

}
